<?php declare(strict_types=1);
/**
 * @author      xianganyall <xianganyall@gmail.com>
 * @copyright   2023-2025 owner
 **/

namespace Srv\Scripts\Tools;

use Srv\Libs\Boot;
use Srv\Libs\Common\CommJson;

require_once dirname(__DIR__, 2).'/Libs/Boot.php';
Boot::runConf('');

$newFilePath        = Boot::getTmpPath().'apis.json';
$oldFilePath        = Boot::getRootPath().'../xctv-backend-apis/apis.json';
$newData            = CommJson::decodeArray(@file_get_contents($newFilePath));
$oldData            = CommJson::decodeArray(@file_get_contents($oldFilePath));

$html               = '';
$resMessageData     = [];
compareEnumList($newData['messageList'], $oldData['messageList'], $resMessageData);
foreach ($newData['groupList'] as $groupName => $groupItemList){
    $urlMaxSize         = 32;
    $fieldMaxSize       = 22;
    $resApiData         = [];
    if(!compareListApi($newData['groupList'][$groupName]['apis']??[], $oldData['groupList'][$groupName]['apis']??[], 'url', ['auth', 'comment', 'codes', 'inputs', 'outputs'], $resMessageData, $resApiData)){
        continue;
    }
    $html               .= "\n".'Apis: ['.$groupName."]\n";
    foreach ($resApiData as $resApiType => $resApiItemList){
        if(count($resApiItemList) < 1) continue;
        switch ($resApiType){
            case 'del':{
                foreach ($resApiItemList as $resItem) $html .= str_pad('Del: /'.$resItem['url'], $urlMaxSize, ' ', STR_PAD_RIGHT).' // '.$resItem['comment']."\n";
                break;
            }
            case 'add':{
                foreach ($resApiItemList as $resItem) $html .= str_pad('Add: /'.$resItem['url'], $urlMaxSize, ' ', STR_PAD_RIGHT).' // '.$resItem['comment']."\n";
                break;
            }
            case 'chg':{
                foreach ($resApiItemList as $resItemUrl => $resItem){
                    $html   .= str_pad('Chg: /'.$resItemUrl, $urlMaxSize, ' ', STR_PAD_RIGHT).' // '.$resItem['comment']."\n";
                    if(isset($resItem['inputs'])){
                        switch ($resItem['inputs']['type']??''){
                            case 'del':{ $html .= ' -delDTO: '.($resItem['inputs']['old'][0]??'')."\n"; break; }
                            case 'add':{ $html .= ' +addDTO: '.($resItem['inputs']['new'][0]??'')."\n"; break; }
                            case 'chg':{ $html .= ' *chgDTO: '.($resItem['inputs']['new'][0]??'').' <- '.($resItem['inputs']['old'][0]??'')."\n"; break; }
                        }
                    }
                    if(isset($resItem['outputs'])){
                        switch ($resItem['outputs']['type']??''){
                            case 'del':{ $html .= ' -delVO: '.($resItem['outputs']['old'][0]??'')."\n"; break; }
                            case 'add':{ $html .= ' +addVO: '.($resItem['outputs']['new'][0]??'')."\n"; break; }
                            case 'chg':{ $html .= ' *chgVO: '.($resItem['outputs']['new'][0]??'').' <- '.($resItem['outputs']['old'][0]??'')."\n"; break; }
                        }
                    }
                }
                break;
            }
            case 'chgList':{
                foreach ($resApiItemList as $resItemUrl => $resItem){
                    $html   .= str_pad('Chg: /'.$resItemUrl, $urlMaxSize, ' ', STR_PAD_RIGHT).' // '.$resItem['comment']."\n";
                    if(isset($resItem['inputs']) && is_array($resItem['inputs']) && count($resItem['inputs']) > 0) foreach ($resItem['inputs'] as $chgListType => $chgListItem){
                        if(count($chgListItem) < 1) continue;
                        switch ($chgListType){
                            case 'del':{ foreach ($chgListItem as $chgListItemVal) $html .= ' -delDTOs: '.str_pad($chgListItemVal['name']??'', $fieldMaxSize, ' ', STR_PAD_RIGHT).' // '.($chgListItemVal['comment']??'')."\n"; break; }
                            case 'add':{ foreach ($chgListItem as $chgListItemVal) $html .= ' +addDTOs: '.str_pad($chgListItemVal['name']??'', $fieldMaxSize, ' ', STR_PAD_RIGHT).' // '.($chgListItemVal['comment']??'')."\n"; break; }
                            case 'chg':{ $html .= ' *chgDTOs: '.implode(', ', array_keys($chgListItem))."\n"; break; }
                        }
                    }
                    if(isset($resItem['outputs']) && is_array($resItem['outputs']) && count($resItem['outputs']) > 0) foreach ($resItem['outputs'] as $chgListType => $chgListItem){
                        if(count($chgListItem) < 1) continue;
                        switch ($chgListType){
                            case 'del':{ foreach ($chgListItem as $chgListItemVal) $html .= ' -delVOs: '.str_pad($chgListItemVal['name']??'', $fieldMaxSize, ' ', STR_PAD_RIGHT).' // '.($chgListItemVal['comment']??'')."\n"; break; }
                            case 'add':{ foreach ($chgListItem as $chgListItemVal) $html .= ' +addVOs: '.str_pad($chgListItemVal['name']??'', $fieldMaxSize, ' ', STR_PAD_RIGHT).' // '.($chgListItemVal['comment']??'')."\n"; break; }
                            case 'chg':{ $html .= ' *chgVOs: '.implode(', ', array_keys($chgListItem))."\n"; break; }
                        }
                    }
                }
                break;
            }
        }
    }
}
// Enum
$enumHtml           = '';
$resEnumData        = [];
$resEnumResult      = compareEnumList($newData['enumList'], $oldData['enumList'], $resEnumData);
if($resEnumResult && count($resEnumData) > 0){
    $enumHtml       .= "\nEnum:\n";
    $nameMaxSize    = 16+42;
    foreach ($resEnumData as $resEnumType => $resEnumItemList){
        switch ($resEnumType){
            case 'del':{
                foreach ($resEnumItemList as $resItemName => $resItem) $enumHtml .= str_pad('Del: '.$resItemName, $nameMaxSize, ' ', STR_PAD_RIGHT).' // '.$resItem['comment']."\n";
                break;
            }
            case 'add':{
                foreach ($resEnumItemList as $resItemName => $resItem) $enumHtml .= str_pad('Add: '.$resItemName, $nameMaxSize, ' ', STR_PAD_RIGHT).' // '.$resItem['comment']."\n";
                break;
            }
            case 'chg':{
                foreach ($resEnumItemList as $resItemName => $resItem){
                    if(isset($resItem['comment'])) unset($resItem['comment']);
                    if(count($resItem) < 1) continue;
                    $enumHtml   .= str_pad('Chg: '.$resItemName, $nameMaxSize, ' ', STR_PAD_RIGHT)."\n";
                    if(isset($resItem['fields'])){
                        foreach ($resItem['fields'] as $fieldType => $fieldItemList) if(count($fieldItemList) > 0) switch ($fieldType){
                            case 'del':{ foreach ($fieldItemList as $fieldItem) $enumHtml .= str_pad(' -del: '.$fieldItem['name'], $nameMaxSize, ' ', STR_PAD_RIGHT).' // '.$fieldItem['comment']."\n"; break; }
                            case 'add':{ foreach ($fieldItemList as $fieldItem) $enumHtml .= str_pad(' +add: '.$fieldItem['name'], $nameMaxSize, ' ', STR_PAD_RIGHT).' // '.$fieldItem['comment']."\n"; break; }
                            case 'chg':{ $enumHtml .= ' *chg: '.implode(', ', array_keys($fieldItemList))."\n"; break; }
                        }
                    }
                }
                break;
            }
        }
    }
}
// 错误代码
$errorCodeHtml          = '';
$resErrorCodeData       = [];
$resErrorCodeResult     = compareList($newData['errCodeList'], $oldData['errCodeList'], 'key', ['code', 'comment'], $resErrorCodeData);
if($resErrorCodeResult && count($resErrorCodeData) > 0){
    $errorCodeHtml      .= "\nErrorCode:\n";
    $codeKeyMaxSize     = 16+42;
    foreach ($resErrorCodeData as $resErrorCodeType => $resErrorCodeItem){
        if(count($resErrorCodeItem) < 1) continue;
        switch ($resErrorCodeType){
            case 'del':{
                foreach ($resErrorCodeItem as $resItem){
                    $errorCodeHtml .= str_pad('Del: '.$resItem['code'].' '.$resItem['key'], $codeKeyMaxSize, ' ', STR_PAD_RIGHT).' // '.$resItem['comment']."\n";
                }
                break;
            }
            case 'add':{
                foreach ($resErrorCodeItem as $resItem){
                    $errorCodeHtml .= str_pad('Add: '.$resItem['code'].' '.$resItem['key'], $codeKeyMaxSize, ' ', STR_PAD_RIGHT).' // '.$resItem['comment']."\n";
                }
                break;
            }
            case 'chg':{
                foreach ($resErrorCodeItem as $resItemKey => $resItem){
                    if(isset($resItem['code'])) $errorCodeHtml .= str_pad('Chg: '.$resItem['code']['new'].' '.$resItemKey, $codeKeyMaxSize, ' ', STR_PAD_RIGHT).' // 原Code:'.$resItem['code']['old']."\n";
                }
                break;
            }
        }
    }
}
$resHtml    = trim($html.$enumHtml.$errorCodeHtml);
if(strlen($resHtml) > 0){
    echo $resHtml."\n\n";
}else{
    echo "No Changes\n\n";
}

function compareEnumList(array $newData, array $oldData, array &$resData):bool
{
    $diffNewAddData     = [];
    $diffChangeData     = [];
    $diffDeleteData     = [];
    if(count($newData) > 0) foreach ($newData as $newKey => $newItem){
        if(isset($oldData[$newKey])){
            $oldItem        = $oldData[$newKey];
            $chgItem        = [];
            $keyName        = 'comment';
            if(!isset($newItem[$keyName]) && !isset($oldItem[$keyName])) continue;
            if(isset($newItem[$keyName])){
                if(isset($oldItem[$keyName])){  // change
                    if($newItem[$keyName] !== $oldItem[$keyName]) $chgItem[$keyName] = ['type' => 'chg', 'new' => $newItem[$keyName], 'old' => $oldItem[$keyName]];
                }else{  // add
                    $chgItem[$keyName] = ['type' => 'add', 'new' => $newItem[$keyName], 'old' => null];
                }
            }else{
                if(isset($oldItem[$keyName])){  // del
                    $chgItem[$keyName] = ['type' => 'del', 'new' => null, 'old' => $oldItem[$keyName]];
                }
            }
            $resDataFields  = [];
            if(compareList($newItem['fields'], $oldItem['fields'], 'name', ['value', 'comment'], $resDataFields)){
                $chgItem['fields'] = $resDataFields;
            }
            if(count($chgItem) > 0) $diffChangeData[$newKey] = $chgItem;
            unset($oldData[$newKey]);
        }else{
            $diffNewAddData[$newKey]    = $newItem;
        }
    }
    if(count($oldData) > 0) $diffDeleteData = $oldData;
    $resData    = ['del' => $diffDeleteData, 'add' => $diffNewAddData, 'chg' => $diffChangeData];
    return count($diffNewAddData) > 0 || count($diffChangeData) > 0 || count($diffDeleteData) > 0;
}

function compareList(array $newData, array $oldData, string $keyIndex, array $keyList, array &$resData):bool
{
    if(strlen($keyIndex) < 1) return false;
    $diffNewAddData     = [];
    $diffChangeData     = [];
    $diffDeleteData     = [];
    $oldDataList        = array_column($oldData, null, $keyIndex);
    if(count($newData) > 0) foreach ($newData as $newItem){
        if(!isset($newItem[$keyIndex])) continue;
        $keyIndexVal    = $newItem[$keyIndex];
        if(isset($oldDataList[$keyIndexVal])){
            $oldItem    = $oldDataList[$keyIndexVal];
            $chgItem    = [];
            foreach ($keyList as $keyName){
                if(!isset($newItem[$keyName]) && !isset($oldItem[$keyName])) continue;
                if(isset($newItem[$keyName])){
                    if(isset($oldItem[$keyName])){  // change
                        $newItemVal     = $newItem[$keyName];
                        $oldItemVal     = $oldItem[$keyName];
                        if(is_array($newItemVal)) $newItemVal = CommJson::encodeArray($newItem[$keyName]);
                        if(is_array($oldItemVal)) $oldItemVal = CommJson::encodeArray($oldItem[$keyName]);
                        if($newItemVal !== $oldItemVal){
                            $chgItem[$keyName] = ['type' => 'chg', 'new' => $newItem[$keyName], 'old' => $oldItem[$keyName]];
                        }
                    }else{  // add
                        $chgItem[$keyName] = ['type' => 'add', 'new' => $newItem[$keyName], 'old' => null];
                    }
                }else{
                    if(isset($oldItem[$keyName])){  // del
                        $chgItem[$keyName] = ['type' => 'del', 'new' => null, 'old' => $oldItem[$keyName]];
                    }
                }
            }
            if(count($chgItem) > 0) $diffChangeData[$keyIndexVal] = $chgItem;
            unset($oldDataList[$keyIndexVal]);
        }else{
            $diffNewAddData[]   = $newItem;
        }
    }
    if(count($oldDataList) > 0) $diffDeleteData = array_values($oldDataList);
    $resData    = ['del' => $diffDeleteData, 'add' => $diffNewAddData, 'chg' => $diffChangeData];
    return count($diffNewAddData) > 0 || count($diffChangeData) > 0 || count($diffDeleteData) > 0;
}

function compareListApi(array $newData, array $oldData, string $keyIndex, array $keyList, array $resMessageData, array &$resData):bool
{
    if(strlen($keyIndex) < 1) return false;
    $diffNewAddData     = [];
    $diffChangeData     = [];
    $diffDeleteData     = [];
    $listChangeData     = [];
    $oldDataList        = array_column($oldData, null, $keyIndex);
    if(count($newData) > 0) foreach ($newData as $newItem){
        if(!isset($newItem[$keyIndex])) continue;
        $keyIndexVal    = $newItem[$keyIndex];
        if(isset($oldDataList[$keyIndexVal])){
            $oldItem    = $oldDataList[$keyIndexVal];
            $chgItem    = [];
            foreach ($keyList as $keyName){
                if(!isset($newItem[$keyName]) && !isset($oldItem[$keyName])) continue;
                if(isset($newItem[$keyName])){
                    if(isset($oldItem[$keyName])){  // change
                        $newItemVal     = $newItem[$keyName];
                        $oldItemVal     = $oldItem[$keyName];
                        if(is_array($newItemVal)) $newItemVal = CommJson::encodeArray($newItem[$keyName]);
                        if(is_array($oldItemVal)) $oldItemVal = CommJson::encodeArray($oldItem[$keyName]);
                        if($newItemVal !== $oldItemVal){
                            if(is_array($newItem[$keyName]) && is_array($oldItem[$keyName])){
                                if(count($oldItem[$keyName]) > 0){
                                    if(count($newItem[$keyName]) > 0){ // chg
                                        $chgItem[$keyName] = ['type' => 'chg', 'new' => $newItem[$keyName], 'old' => $oldItem[$keyName]];
                                    }else{  // del
                                        $chgItem[$keyName] = ['type' => 'del', 'new' => null, 'old' => $oldItem[$keyName]];
                                    }
                                }else{
                                    if(count($newItem[$keyName]) > 0){ // add
                                        $chgItem[$keyName] = ['type' => 'add', 'new' => $newItem[$keyName], 'old' => null];
                                    }
                                }
                            }else{  // chg
                                $chgItem[$keyName] = ['type' => 'chg', 'new' => $newItem[$keyName], 'old' => $oldItem[$keyName]];
                            }
                        }
                    }else{  // add
                        $chgItem[$keyName] = ['type' => 'add', 'new' => $newItem[$keyName], 'old' => null];
                    }
                }else{
                    if(isset($oldItem[$keyName])){  // del
                        $chgItem[$keyName] = ['type' => 'del', 'new' => null, 'old' => $oldItem[$keyName]];
                    }
                }
            }
            if(count($chgItem) > 0){
                if(!isset($chgItem['comment']) && isset($newItem['comment'])) $chgItem['comment'] = $newItem['comment'];
                $diffChangeData[$keyIndexVal] = $chgItem;
            }else{
                // 判断DTO和VO的变化
                if(isset($newItem['inputs']) && count($newItem['inputs']) > 0) foreach ($newItem['inputs'] as $inputDTO){
                    if(isset($resMessageData['del'][$inputDTO])){
                        $listChangeData[$keyIndexVal] = ['inputs' => $resMessageData['del'][$inputDTO]['fields']??[], 'comment' => $newItem['comment']??''];
                    }else if(isset($resMessageData['add'][$inputDTO])){
                        $listChangeData[$keyIndexVal] = ['inputs' => $resMessageData['add'][$inputDTO]['fields']??[], 'comment' => $newItem['comment']??''];
                    }else if(isset($resMessageData['chg'][$inputDTO])){
                        $listChangeData[$keyIndexVal] = ['inputs' => $resMessageData['chg'][$inputDTO]['fields']??[], 'comment' => $newItem['comment']??''];
                    }
                }
                if(isset($newItem['outputs']) && count($newItem['outputs']) > 0) foreach ($newItem['outputs'] as $outputVO){
                    if(isset($resMessageData['del'][$outputVO])){
                        $listChangeData[$keyIndexVal] = ['outputs' => $resMessageData['del'][$outputVO]['fields']??[], 'comment' => $newItem['comment']??''];
                    }else if(isset($resMessageData['add'][$outputVO])){
                        $listChangeData[$keyIndexVal] = ['outputs' => $resMessageData['add'][$outputVO]['fields']??[], 'comment' => $newItem['comment']??''];
                    }else if(isset($resMessageData['chg'][$outputVO])){
                        $listChangeData[$keyIndexVal] = ['outputs' => $resMessageData['chg'][$outputVO]['fields']??[], 'comment' => $newItem['comment']??''];
                    }
                }
            }
            unset($oldDataList[$keyIndexVal]);
        }else{
            $diffNewAddData[]   = $newItem;
        }
    }
    if(count($oldDataList) > 0) $diffDeleteData = array_values($oldDataList);
    $resData    = ['del' => $diffDeleteData, 'add' => $diffNewAddData, 'chg' => $diffChangeData, 'chgList' => $listChangeData];
    return count($diffNewAddData) > 0 || count($diffChangeData) > 0 || count($diffDeleteData) > 0 || count($listChangeData) > 0;
}